#include "SVector.h"

#include <iostream>
#include <iomanip>

#include "ExpressionResolutor.h"

void SVector::AddItem( SValueSp value )
{
	if (!value)
	{
		throw SArgumentException("The value cannot be null.");
	}

	// value must be of the correct type

	if ( value->GetType() != typeDescriptor->GetItemType() )
	{
		throw SArgumentException("The value is the incorrect type.");
	}
	if ( value->IsComplexType() )
	{
		if ( value->GetTypeName() != typeDescriptor->GetItemTypeDescriptor()->GetFullyQualifiedName() )
		{
			throw SArgumentException("Value is of the same type but with a different type descriptor");
		}
	}

	values->push_back(value);
}

std::string SVector::ToString( string format )
{
	ostringstream os;

	int len = GetLength();
	if ( len == 0)
	{
		return "{}";
	}

	// Pretty print format (show each element in a different line)
	if ( format == "pp")
	{
		os << endl << "{" << endl;

		for(int i=0; i<len; ++i)
		{
			SValueSp value = values->at(i);

			if ( i > 0)
			{
				// All the members are on the same line
				os << "," << endl;
			}

			os << Utility::GetPrettyPrintVar(value);
		}

		os << endl << "}";
	}

	if ( format.empty() )
	{
		os << "{ ";

		for(int i=0; i<len; ++i)
		{
			SValueSp value = values->at(i);

			if ( i > 0)
			{
				// All the members are on the same line
				os << ", ";
			}

			os << value->ToString();
		}

		os << " }";
	}
	else if ( format == "x" )
	{
		if ( typeDescriptor->GetItemType() != STypeByte )
		{
			throw SArgumentException("Format 'x' is applicable only on SByte vectors.");
		}

		os << "0x";

		for(int i=0; i<len; ++i)
		{
			SByteSp value = values->at(i)->As<SByte>();

			// All the members are on the same line
			os << uppercase << hex << setw(2) << setfill('0') << (unsigned int) value->Get();
		}
	}
	else if ( format == "nx" )
	{
		if ( typeDescriptor->GetItemType() != STypeByte )
		{
			throw SArgumentException("Format 'nx' is applicable only on SByte vectors.");
		}

		for(int i=0; i<len; ++i)
		{
			SByteSp value = values->at(i)->As<SByte>();

			// All the members are on the same line
			os << uppercase << hex << setw(2) << setfill('0') << (unsigned int) value->Get();
		}
	}	
	else if ( format == "a" )
	{
		if ( typeDescriptor->GetItemType() != STypeByte )
		{
			throw SArgumentException("Format 'a' is applicable only on SByte vectors.");
		}

		for(int i=0; i<len; ++i)
		{
			SByteSp value = values->at(i)->As<SByte>();

			unsigned char c = value->Get();

			if ( Utility::IsPrintable(c) )  // check if c is a printable character
			{
				os << c;
			}
			else
			{
				os << ".";
			}
		}
	}

	smatch m;

	// format: <num>x
	if ( regex_search(format, m, regex("(\\d+)x") ) )
	{
		if ( typeDescriptor->GetItemType() != STypeByte )
		{
			throw SArgumentException("Format '<num>x' is applicable only on SByte vectors.");
		}

		int n;

		try
		{
			n = lexical_cast<int>( m[1] );
		}
		catch(bad_lexical_cast&)
		{
			n = 1;
		}


		os << "0x";

		for(int i=0; i<n; ++i)
		{
			SByteSp value = values->at(i)->As<SByte>();
			os << uppercase << hex << setw(2) << setfill('0') << (unsigned int) value->Get();
		}
	}

	// format: <num>nx
	if ( regex_search(format, m, regex("(\\d+)nx") ) )
	{
		if ( typeDescriptor->GetItemType() != STypeByte )
		{
			throw SArgumentException("Format '<num>nx' is applicable only on SByte vectors.");
		}

		int n;

		try
		{
			n = lexical_cast<int>( m[1] );
		}
		catch(bad_lexical_cast&)
		{
			n = 1;
		}

		for(int i=0; i<n; ++i)
		{
			SByteSp value = values->at(i)->As<SByte>();
			os << uppercase << hex << setw(2) << setfill('0') << (unsigned int) value->Get();
		}
	}

	return os.str();
}

SVectorSp SVector::Parse( SVectorTypeDescriptorSp typeDescriptor, string s)
{
	SValueSpMapSp vars(new SValueSpMap);

	return Parse(typeDescriptor, s, vars);
}

SVectorSp SVector::Parse( SVectorTypeDescriptorSp typeDescriptor, string s, SValueSpMapSp vars  )
{
	if (!typeDescriptor || !typeDescriptor->IsValid())
	{
		throw SArgumentException("The vector type descriptor is not valid.");
	}

	trim(s);

	smatch m;
	SVectorSp v = SVector::Create( typeDescriptor );

	// Check if the string is an exadecimal representation of a byte vector
	// This is a particular case but extremely usefull!!
	if ( regex_match( s, m, regex("0x([0-9a-fA-F]+)") ) )
	{
		string xs = m[1];
		
		for(int i=0; i < (int) xs.length(); i+=2)
		{
			unsigned char n = (unsigned char) Utility::HexToDec( xs.substr(i, 2) );
			v->AddItem( SByte::Create(n) );
		}

		return v;
	}

	// If the string is an empty vector { }
	if ( regex_match( s, m, regex("\\{\\s*\\}") ) )
	{
		return v;
	}

	if ( !regex_match( s, m, regex("\\{(.*)\\}") ) )
	{
		throw SParseException("Syntax error: a vector must be put inside two brackets.");
	}

	string stringArgs = m[1];

	// Split arguments
	StringVector args;     
	Utility::MergeArgs(stringArgs, args);

	// Now in args there are all the single items
	SType itemType = typeDescriptor->GetItemType();
	TypeDescriptorSp itemTypeDescriptor = typeDescriptor->GetItemTypeDescriptor();

	try
	{
		BOOST_FOREACH(string arg, args)
		{
			trim( arg );
		
			if (Utility::IsSimpleType( itemType ))
			{				
				v->AddItem( ExpressionResolutor::Resolve(arg, vars, itemType) );
			}
			else
			{
				v->AddItem( ExpressionResolutor::Resolve(arg, vars, itemTypeDescriptor) );			
			}				
		}
	}
	catch(const SException& ex)
	{
		cout << "Error Message: " << ex.Message() << endl;
		throw SParseException("Syntax error: " + s + " is not a valid SVector literal.");
	}	

	return v;
}

SVector::SVector( SVectorTypeDescriptorSp typeDescriptor )
{
	this->typeDescriptor = typeDescriptor;
	values = SValueSpVectorSp( new SValueSpVector() );
}

SValueSp SVector::Get( int index )
{
	if (index < 0 || index >= GetLength())
	{
		throw SArgumentException("The index is out of range.");
	}

	return values->at(index);
}

SVectorSp SVector::Create( SVectorTypeDescriptorSp typeDescriptor )
{
	if ( typeDescriptor && !typeDescriptor->IsValid() )
	{
		throw SArgumentException("The type descriptor is not valid.");
	}

	return SVectorSp(new SVector(typeDescriptor));
}